原理

利用Top Chunk在分配时的bug,改写其size,实现任意地址写。

利用步骤

  1. 改写Top Chunk的size为一个很大的值,通常可以是-1,这将被转换成无符号的最大值。
  2. 调用malloc(targetAddress - wilderness - 2sizeof(long))申请一个新的chunk。其中targetAddress是稍后需要控制的地址;2sizeof(long)是2倍的heap metadata的大小;wilderness是Top Chunk的起始地址,在main_arena中也会有引用。
  3. 再次malloc,这个malloc出来的chunk地址就是targetAddress,就能达成任意地址的写了。

工作原理

在libc启用tcache后,free后满足一定size的chunk会优先插入tcache。如下图,每个index下是一个单向链表。每个链表最大长度为7个,满了后进入fastbin或unsortedbin。malloc时,如果满足size,会优先从tcache中取回,若tcache中已空则查看fastbin、smallbin或unsortedbin。

Read more »

此漏洞可用于修改任意地址的值为一个很大的值(实际为unsorted bin的头地址)。

利用思路为覆盖位于unsorted bin中最后一个chunk的BK指针,令其指向我们需要修改其值的地址 - 0x10的位置。libc在重新Malloc完所有原先位于unsorted bin中的chunk后,会将该地址的内存值改写为Unsorted bin的header的首地址。这是一个非常大的值,可用于修改 heap 中的全局变量global_max_fast(它控制了fastbin的最大容量),若将其改为一个非常大的值,则可以把问题转化为fasbin攻击。

当free较小size的chunk时,会被分配到fastbin。相同容量的fastbin里的chunk被用单向链表存储起来,原数据区的第一行被用来存放FD指针。

如上图所示,第一次重新malloc如果命中size,将从fastbin上取回(a),并将(b)放到链表的开头。

如果我们可以覆盖(a)的FD,指向另一块内存,那再次malloc后就会得到位于那块内存地址的chunk。

如果Fake Chunk为某个标准函数的GOT地址,那么我们通过2次malloc得到这个chunk后,就能改写GOT地址为任意其他函数了。

何为Unlink()

Unlink()是free()调用中的一步。当free某个chunk时,程序会查看其上一个chunk是否也是free,如果是,就会把上一个chunk从bin的双向链表上移除(这就是unlink),并与当前free的chunk合并。这里要注意,如果上一个chunk在fastbin或tcache,是不会发生unlink和合并的。这是因为他们被设计成非常高效的缓存,避免诸如合并的多余操作。

从结论来说,这个漏洞能够使我们写任意地址的数据。

Read more »

在C/C++中,可以通过malloc等方法分配heap的内存空间。分配空间本身没有风险,风险来自于释放该空间。在此针对libc,介绍heap的漏洞和利用。

在介绍如何利用漏洞前,首先简单了解一下heap的数据结构。

内存布局

整个heap段与stack相反,是从低地址向高地址增长的。

Read more »

理论背景

在调用动态链接库中的函数时,会触发_dl_runtime_resolve进行延迟绑定。绑定过程需要2个变量:link_map和offset/index。前者在程序中为一静态变量,可通过其访问各dynamic section。后者为rel table的一个offset,用以找到rel结构体。通过rel结构体的r_info字段,可找到对应symbol table的结构体。通过symbol结构体的st_name字段,可找到string table中对应的函数名string。最后通过这个函数名,在动态链接库中找到其对应的GOT地址,写回rel结构体的r_offset字段,并执行这个GOT函数。

Read more »

Sigreturn系统调用是为了多进程管理的一种内核机制。一般是和signal成对出现。在执行signal时,系统会在栈顶构建一个signal栈,随后在执行sigreturn时会从这个栈恢复数据。如果越过signal,直接调用sigreturn,那通过手动构建signal frame并push到栈顶,使其rip为addr(syscall),同时写入syscall需要的各寄存器数值到signal frame,那么在执行完sigreturn时,就能跳转到rip指定的地址执行系统调用。

signal frame的定义如下:


+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

Overview

局部变量在栈溢出后,先经过Saved EBP,再到达Saved EIP。如果能溢出的字节数有限,以至于只能篡改到Saved EBP的数据,就无法通过修改Saved EIP里的数据控制程序走向。通过篡改Saved EBP的数据,在ret后,ESP会指向Saved EBP中的地址,从而实现栈的迁移。

实施条件

令Saved EIP所指向的执行语句为:
leave; ret

leave等效于:


mov ESP, EBP
pop EBP

ret等效于:


pop EIP

实施方法

将Saved EBP中的数据修改为Attack Payload的地址。这样执行”mov ESP, EBP”时就会使ESP指向被篡改的地址区域。
构建位于Attack Payload地址处的exploit:
Payload最开始必须为4 dummy bytes(因为后面接上“pop EBP”,需要补4各字节)。在第二次ret(第一次执行了leave;ret,第二次执行恶意代码)后,
开始紧接其后的payload:

  • A valid ONE_GADGET address.
  • The address of system() followed **by 4 junk bytes** and the address of "/bin/sh" (x86 bits).
  • The address of a jump esp; gadget (ret2esp) followed by the shellcode to execute.
  • Some ROP chain

原理

使用format parameter可读取Stack上的数据,且通过%n可以写入指定内存。


int main(int argc, char *argv[])
{
	char user_input[100];
	/* other variable definitions and statements */
	scanf("%s", user_input); /* getting a string from user */
	printf(user_input); /* Vulnerable place */
	return 0;
}

当输入字符串为”\x10\x01\x48\x08 %x %x %x %x %s”时,

printf会打印并移动4次指针(%x的数量),到达最后一个字节处后打印出这个地址0x10014808的内容(直到NULL)。用此方法可以泄露任意地址的字符串内容。关键在于找到printf()的第一个参数和user_input之间的offset,这样就能通过输入offset个数的%x定位到用户输入的payload的地址。

当输入字符串为”\x10\x01\x48\x08 %x %x %x %x %n”时,

printf会打印并移动4次指针(%x的数量),到达最后一个字节处后将已打印出的字符数写入地址0x10014808。

Read more »
0%